Spring Cloud系列教程(2.1) - Eureka(服务注册中心)

1. 什么是服务?

在软件开发领域,到处可见服务一词,而我们这里说的服务,我个人觉得与“微服务”这个词是分不开。微服务架构近两年兴起,Spring Cloud正是微服务架构的一套解决方案或者框架。

那什么是微服务呢?

官方定义如下:

The microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services , which may be written in different programming languages and use different data storage technologies.
– James Lewis and Martin Fowler

想更多了解,可以阅读Martin Fowler的《Microservices》.

简单的说,微服务架构就是将一个完整的应用从数据存储开始垂直拆分成多个不同的服务,每个服务都能独立部署、独立维护、独立扩展,服务与服务间通过诸如RESTful API或者RPC等方式互相调用。

2. Service Registry(服务注册与发现)

在了解什么是服务之后,下面我们来看看要如何使用Spring Cloud去实现服务的注册和发现。

要实现服务的注册的发现,Spring Cloud提供的不止一套方案,其中Spring Cloud Zookeeper, Spring Cloud ConsulSpring Cloud Eureka都可以实现,而这里我们要用Eureka去实现。

接下来,让我们动手写一些代码做一下尝试:

2.1 Service Registry ( 服务注册中心 )

怎么去实现一个服务注册中心,只需要使用spring-cloud-starter-eureka-server便可轻松完成。

首先,创建一个基础的Spring Boot工程,并在pom.xml中引入需要的依赖内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.SR6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

然后,使用@EnableEurekaServer注解来启动一个服务注册中心:

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

public static void main(String[] args) {
new SpringApplicationBuilder(EurekaServerApplication.class).web(true).run(args);
}
}

在默认配置下,服务注册中心有个自注册的行为,也就是说它会默认把自己当成客户端注册给自己,而在单实例运行情况下,我们不需要这样的自注册行为,所以我们可以通过配置关闭这种行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server:
port: 8761

eureka:
instance:
hostname: localhost
client:
#是否将eureka自身作为应用注册到eureka注册中心
registerWithEureka: false
#启动时是否去fetch其他的注册中心
#为true时,可以启动,但报异常:Cannot execute request on any known server
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

服务注册中心是自带UI界面的,默认情况下,使用8761端口。如上所示,我们同样可以更改配置文件来修改服务器端口,例如:1234,1111,等等。

这样,我们就可以启动我们的应用,启动完成后,我们可以访问:http://localhost:8761

可以看到下面的页面,不过现在还没有任何服务注册进来:

示例代码: Github : eureka-server

2.2 High Availability ( 高可用服务注册中心 )

Eureka Server作为一个Service Registry,不仅可以单实例运行,还可以通过运行多个实例并且相互注册的方式来实现高可用性和可扩展性,运行的多个实例,互为对等体,而这种行为是默认行为,我们无需做很多工作,只需要在每个对等体里面添加一个有效的serviceUrl即可。

下面我们以上面的例子为基础,动手尝试一下:
修改application.yml文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
---
spring:
profiles: peer1

server:
port: 8761

eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: http://peer2/eureka/

---
spring:
profiles: peer2

server:
port: 8762

eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: http://peer1/eureka/

修改/etc/hosts文件

因为我是在本机一台server上做测试,所以要修改/etc/hosts文件做一下IP和domain的映射,如果你有两台机器测试,此步可免。

1
2
127.0.0.1 peer1
127.0.0.1 peer2

依次来运行一下每个profile, 先运行peer1,然后打开http://localhost:8761

可以看到,在registered-replicas里面已经有了peer2节点了,以为这个时候peer2还未启动,所以peer2也在unavailable-replicas里面,而available-replicas里面目前

再来运行peer2,然后打开http://localhost:8762

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
at com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient.execute(RetryableEurekaHttpClient.java:111) ~[eureka-client-1.4.12.jar:1.4.12]
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) ~[eureka-client-1.4.12.jar:1.4.12]
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator$1.execute(EurekaHttpClientDecorator.java:59) ~[eureka-client-1.4.12.jar:1.4.12]
at com.netflix.discovery.shared.transport.decorator.SessionedEurekaHttpClient.execute(SessionedEurekaHttpClient.java:77) ~[eureka-client-1.4.12.jar:1.4.12]
at com.netflix.discovery.shared.transport.decorator.EurekaHttpClientDecorator.register(EurekaHttpClientDecorator.java:56) ~[eureka-client-1.4.12.jar:1.4.12]
at com.netflix.discovery.DiscoveryClient.register(DiscoveryClient.java:815) ~[eureka-client-1.4.12.jar:1.4.12]
at com.netflix.discovery.InstanceInfoReplicator.run(InstanceInfoReplicator.java:104) [eureka-client-1.4.12.jar:1.4.12]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_66]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_66]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_66]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.8.0_66]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_66]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_66]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_66]

热评文章